# coding:utf-8
# 兼容python2像python3的除法
from __future__ import division
import time, uuid, base64
import requests, qrcode
from xml.etree import ElementTree as etree
from io import BytesIO
from payment import md5_encode
import config

###############################################################################
# 微信文档地址 ################################################################
###############################################################################
"""
https://pay.weixin.qq.com/wiki/doc/api/index.html
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1     公众号支付文档
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1    扫码支付文档   
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1   APP支付文档
支付结果通知：通知频率为15/15/30/180/1800/1800/1800/1800/3600    单位：秒
"""

###############################################################################
# 微信接口地址 ################################################################
###############################################################################
# 统一下单接口 （不需要证书）
UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"
# 查询订单 （不需要证书）
ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery"
# 关闭订单 （不需要证书）
CLOSEORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"
# 申请退款 （需要证书） （https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3）
refund_url = "https://api.mch.weixin.qq.com/secapi/pay/refund"
# 查询退款 （不需要证书）
REFUNDQUERY_URL = "https://api.mch.weixin.qq.com/pay/refundquery"


###############################################################################
# 微信支付 Common #############################################################
###############################################################################
def convert_penny(unitary):
    """
    将元转为分
    :param unitary:     元
    :return:            分
    """
    penny = unitary * 100
    return int(penny)


def convert_unitary(penny):
    """
    将分转为元
    :param penny:   分
    :return:        元
    """
    unitary = penny / 100
    return unitary


def get_timestamp():
    """
    获取时间戳   秒
    :return: 
    """
    t = time.time()
    timestamp = int(t)
    return timestamp


def get_noncestr():
    """
    获取随机字符串
    :return: 
    """
    noncestr = uuid.uuid4().hex
    return noncestr


def decode_xml(str_xml):
    """
    解析XML
    :return:  返回字典格式
    """
    xml = etree.fromstring(str_xml)
    m = {}
    for x in xml:
        m[x.tag] = x.text
    return m


def to_xml(_dic):
    """
    转换XML
    :param _dic:   字典对象
    :return:      XML字符串
    """
    xml_str = "<xml version='1.0' encoding='UTF-8' standalone='yes'>"
    for k, v in _dic.items():
        xml_str += "<" + str(k) + ">" + str(v) + "</" + str(k) + ">"
    xml_str += "</xml>"
    return xml_str


def make_sign(_dic, trade_type):
    """
    生成签名
    :param _dic:         需要签名的内容
    :param trade_type:  交易类型
    :return: 
    """
    # 根据KEY按ASCII码值排序
    _res = sorted(_dic.items(), key=lambda d: d[0])
    _str = ""
    for r in _res:
        if str(r[0]) != "sign" and str(r[1]) != "":
            _str += str(r[0]) + "=" + str(r[1]) + "&"
    _str += "key=" + config.get_tradetype_value(trade_type, "API_KEY")
    package_sign = md5_encode(_str)
    return package_sign.upper()


def gen_req_args(_dic, trade_type):
    """
    获取请求时的参数
    :param _dic:         参数（字典）
    :param trade_type:  交易类型
    :return: 
    """
    _dic["nonce_str"] = get_noncestr()
    sign = make_sign(_dic, trade_type)
    _dic["sign"] = sign
    xml_str = to_xml(_dic)
    return xml_str


def gen_qrcode(code_url):
    """
    扫码支付时，根据code_url生成二维码
    :param code_url:  如 weixin://wxpay/s/An4baqw
    :return:  返回图片的Base64编码      
    """
    qr = qrcode.QRCode(
        version=5,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(code_url)
    qr.make(fit=True)
    # 获取字节流
    out = BytesIO()
    img = qr.make_image()
    img.save(out, 'PNG')
    return u"data:image/png;base64," + base64.b64encode(out.getvalue()).decode('ascii')


###############################################################################
# 微信支付相关请求 ############################################################
###############################################################################
class BasePay(object):
    def __init__(self, _dic):
        """
        初始化
        :param _dic:  {'APP': {'API_KEY': 'XXX', ……}, ……}
        """
        for k, v in _dic.items():
            if k == "APP":
                config.APP = v
            elif k == "NATIVE":
                config.NATIVE = v
            elif k == "JSAPI":
                config.JSAPI = v
            elif k == "REFUND":
                config.REFUND = v

    def __req_url(self, url, data, timeout):
        _res = requests.post(url, data=data, timeout=timeout)
        xml_str = _res.content
        self.xml_dic = decode_xml(xml_str)
        self.return_code = self.xml_dic.get("return_code")          # = SUCCESS通信成功
        self.return_msg = self.xml_dic.get("return_msg")            # = OK签名成功
        self.result_code = self.xml_dic.get("result_code")          # = SUCCESS交易成功
        self.err_code = self.xml_dic.get("err_code", "")            # 错误代码
        self.err_code_des = self.xml_dic.get("err_code_des", "")    # 错误代码描述

    def unified_order(self, body, out_trade_no, trade_type, total_fee, **params):
        """
        统一下单
        :param body:            商品描述
        :param out_trade_no:    商户订单号
        :param trade_type:      交易类型
        :param total_fee:       总金额 （分）
        :param params: 
        :return: 
        """
        param = {}
        param["body"] = body
        param["out_trade_no"] = out_trade_no
        param["trade_type"] = trade_type
        param["total_fee"] = total_fee
        if trade_type == "APP":
            param.update(config.APP)
        elif trade_type == "NATIVE":
            param.update(config.NATIVE)
        elif trade_type == "JSAPI":
            param.update(config.JSAPI)
        elif trade_type == "REFUND":
            param.update(config.REFUND)
        if "attach" in params.keys():
            param["attach"] = params.get("attach")
        del param["API_KEY"]  # 清除API_KEY
        # 获取请求接口时的参数
        p = gen_req_args(param, trade_type)
        self.__req_url(UNIFIEDORDER_URL, p, 20)
        if self.return_code == "SUCCESS" and self.return_msg == "OK":
            if self.result_code == "SUCCESS":
                result = {"code": 0}
                if trade_type == "APP":
                    m = {
                        "appid": self.xml_dic.get("appid"),
                        "partnerid": self.xml_dic.get("mch_id"),
                        "prepayid": self.xml_dic.get("prepay_id"),
                        "noncestr": self.xml_dic.get("nonce_str"),
                        "package": "Sign=WXPay",
                        "timestamp": get_timestamp()
                    }
                    sign = make_sign(m, trade_type)
                    m["sign"] = sign
                    result["data"] = m
                elif trade_type == "NATIVE":
                    code_url = self.xml_dic.get("code_url")
                    result["code_url"] = code_url
                elif trade_type == "JSAPI":
                    m = {
                        "appId": self.xml_dic.get("appid"),
                        "nonceStr": self.xml_dic.get("nonce_str"),
                        "package": "prepay_id=" + self.xml_dic.get("prepay_id"),
                        "timeStamp": get_timestamp(),
                        "signType": "MD5",
                    }
                    sign = make_sign(m, trade_type)
                    m["paySign"] = sign
                    result["data"] = m
                return result
            else:
                return {"code": self.result_code, "err_code_des": self.err_code_des, "err_code": self.err_code}
        else:
            return {"code": self.return_code, "msg": self.return_msg, "err_code_des": self.err_code_des,
                    "err_code": self.err_code}

    def order_query(self, trade_type, transaction_id=None, out_trade_no=None):
        """
        查询订单号
        :param trade_type:      交易类型
        :param transaction_id:  微信订单号（优先使用）
        :param out_trade_no:    商户订单号
        :return: 
        """
        param = {}
        if transaction_id:
            param["transaction_id"] = transaction_id
        else:
            if out_trade_no:
                param["out_trade_no"] = out_trade_no
            else:
                return {"code": -1, "msg": "微信订单号和商户订单号不能同时为空"}
        if trade_type == "APP":
            conf_d = config.APP
        elif trade_type == "NATIVE":
            conf_d = config.NATIVE
        elif trade_type == "JSAPI":
            conf_d = config.JSAPI
        if conf_d:
            param["appid"] = conf_d.get("appid")
            param["mch_id"] = conf_d.get("mch_id")
        # 获取请求接口时的参数
        p = gen_req_args(param, trade_type)
        self.__req_url(ORDERQUERY_URL, p, 20)
        if self.return_code == "SUCCESS" and self.return_msg == "OK":
            if self.result_code == "SUCCESS":
                result = {"code": 0, "data": self.xml_dic}
                return result
            else:
                return {"code": self.result_code, "err_code_des": self.err_code_des, "err_code": self.err_code}
        else:
            return {"code": self.return_code, "msg": self.return_msg, "err_code_des": self.err_code_des,
                    "err_code": self.err_code}


if __name__ == "__main__":
    from payment import get_out_trade_no
    dic = {
        "APP": {
            "API_KEY": "feichangxian2017727zfnr4270",
            "appid": "wx457b0e5967ac",
            "mch_id": "148637",
            "notify_url":  "http://chenghao.ngrok.io/pay/wxNotifyUrl"
        },
        "NATIVE": {
            "API_KEY": "youpin2017139linye0987456321",
            "appid": "wx0a38e23f5fc6",
            "mch_id": "145155",
            "notify_url": "http://chenghao.ngrok.io/pay/wxNotifyUrl"
        }
    }
    pay = BasePay(dic)
    res = pay.unified_order("测试", get_out_trade_no(), "NATIVE", convert_penny(1))
    code_url = res.get("code_url")
    print(code_url)
    print(gen_qrcode(code_url))
